﻿module AleaEigenCFA.EigenCFA

open Alea.CUDA
open AleaEigenCFA.StaticData

let sw = System.Diagnostics.Stopwatch()

let pfunct = cuda {

    let! createStartStoreKernel = 
        <@ fun  (devStore:DevicePtr<int>)
                scaleExp
                scaleM
                scaleVar ->
            let row = blockDim.y*blockIdx.y + threadIdx.y
            let column = blockDim.x*blockIdx.x + threadIdx.x
            
            if(row < scaleExp && column < scaleM) then 
                if(row < scaleVar) then
                    if(column % scaleM = 0) then
                        devStore.[row*scaleM + column] <- 1
                    else
                        devStore.[row*scaleM + column] <- -1
                else
                    if(column = 0) then
                        devStore.[row*scaleM + column] <- 2
                    else 
                        if(column = 1) then
                            devStore.[row*scaleM + column] <- row - scaleVar + 1
                        else 
                            devStore.[row*scaleM + column] <- -1 @>
                            |> defineKernelFunc
                
    let! CUDAEigenCFAKernel =
        <@ fun (Fun:DevicePtr<int>)
               (Arg1:DevicePtr<int>)
               (Arg2:DevicePtr<int>)
               (Store: DevicePtr<int>)
               (Rep: DevicePtr<int>)
               scaleM
               scaleCall
               scaleCallM
               scaleExp
               scaleLam ->
                   let column = blockDim.x*blockIdx.x + threadIdx.x
                   if(column < scaleCall && blockIdx.y < 2) then
                        let numCall = column
                        let Argi (index:int) : int=  
                            if(index = 0) then Arg1.[numCall]
                            else Arg2.[numCall]
                        let L index = Store.[Fun.[numCall]*scaleM + index]
                        let Li index = Store.[(Argi blockIdx.y)*scaleM + index]
                        let rowStore row column = Store.[row*scaleM + column]
                        let vL j =
                            if(blockIdx.y = 0) then
                                (L j) - 1
                            else
                                (L j) - 1 + scaleLam
                        for j in 1 .. ((L 0) - 1) do
                            if (L j) > 0 then
                                for k in 1 .. ((Li 0) - 1) do
                                    if (Li k) > 0 then
                                        //add var
                                        //error = addVar(&Store[vL[blockIdx.y]*scaleM], Li[k], scaleM, rep, k, Argi[blockIdx.y]);
                                        let mutable isAdd = 1
                                        let addVar = (Li k)
                                        for i in 1 .. ((rowStore (vL j) 0) - 1) do
                                            if((rowStore (vL j) i) = addVar) then 
                                                isAdd <- 0
                                 //       outPut.[numCall + (blockIdx.y)*scaleCall] <- isAdd
                                        if(isAdd > 0) then
                                            Rep.[0] <- Rep.[0] + 1
                                            let tail = (rowStore (vL j) 0)
        //                                        if((rowStore (vL j) 0) >= scaleM) then
        //                                            printf "memEnd!\n"
        //                                        else
                                            Store.[(vL j)*scaleM] <- Store.[(vL j)*scaleM] + 1
                                            Store.[(vL j)*scaleM + tail] <- addVar
             @>
        |> defineKernelFunc

    let divUp num den = (num + den - 1) / den

    return PFunc(fun (m:Module) (ArrayFun:int[]) (ArrayArg1:int[]) (ArrayArg2:int[])->
        let createStartStoreKernel = createStartStoreKernel.Apply m
        let CUDAEigenCFAKernel = CUDAEigenCFAKernel.Apply m
        let n = scaleM*scaleExp
       
        
        use dev_Store = m.Worker.Malloc<int>(n)
        use dev_Fun = m.Worker.Malloc(ArrayFun)
        use dev_Arg1 = m.Worker.Malloc(ArrayArg1)
        use dev_Arg2 = m.Worker.Malloc(ArrayArg2)
        use dev_rep = m.Worker.Malloc<int>(1)
        use dev_outPut = m.Worker.Malloc<int>(scaleCall*2)
        //create startStore
        let thread : dim3 = new dim3(32,32)
        let blocks : dim3 = new dim3(divUp scaleM thread.x, divUp scaleExp thread.y)
        let lpCSSK = LaunchParam(blocks, thread)
        createStartStoreKernel.Launch lpCSSK dev_Store.Ptr scaleExp scaleM scaleVar
        
        //EigenCFA
        let blocksIegenCFA : dim3 = new dim3(divUp scaleCall 1024,2)
        let mutable rep = dev_rep.ToHost().[0]
        let mutable curRep = -2
        //printf "rep = %d curRep = %d \n" rep curRep
        
        let lpECFA = LaunchParam(blocksIegenCFA, new dim3(1024))
        let mutable iter = 1
        while(rep <> curRep) do
//            printf "iter = %d \n" iter
            iter <- iter + 1
            rep <- curRep
            let EigenCFA = 
                CUDAEigenCFAKernel.Launch lpECFA dev_Fun.Ptr dev_Arg1.Ptr dev_Arg2.Ptr dev_Store.Ptr dev_rep.Ptr scaleM scaleCall scaleM scaleExp scaleLam
                let a = dev_rep.ToHost()
                a
            let a = EigenCFA
            curRep <- a.[0]
         //   printf "rep = %d curRep = %d \n" rep curRep
            //System.Console.Read()
            //let dz = dev_Store.ToHost()
            //printfStore dz scaleExp scaleM

        dev_Store.ToHost()
        //dev_outPut.ToHost()

        ) }

let run Fun Arg1 Arg2 numE =
    let mutable fullTime = 0.0
    let numIter = 2
    printf "test %d lam %d call e %d\n" scaleLam scaleCall numE
    for i in 0 .. numIter do
        sw.Start()
        let worker = Engine.workers.DefaultWorker
        use pfuncm = worker.LoadPModule(pfunct)


        let dz = pfuncm.Invoke Fun Arg1 Arg2

        sw.Stop()
        fullTime <- fullTime + (double sw.ElapsedMilliseconds)
    writeResTestInFile pathToResFile "AleaEigenCFA.txt" (fullTime/(float numIter)) (scaleLam * 3 + scaleCall) numE
    ()
    //printfn "%A" dz
    //printf "\n"
    //printfStore dz scaleExp scaleM

    //printfStore dz 1 8

//printf "%A\n" Store
//printf "%A\n" Fun
//printf "%A\n" Arg1
//printf "%A\n" Arg2

let Start maxMem path = 
    let mutable a = 0
    let startAllTests = 
        for subdir in dirs path do
            if a < 3
            then
                a <- a + 1
                if (isAllFilesContains subdir) then
                    pathToFun <- System.IO.Path.Combine(subdir, nameFileFun)
                    pathToArg1 <- System.IO.Path.Combine(subdir, nameFileArg1)
                    pathToArg2 <- System.IO.Path.Combine(subdir, nameFileArg2)
                    pathToScales <- System.IO.Path.Combine(subdir, nameFileScales)

                    let scales = readMapInt pathToScales
                    scaleCall <- scales.[0]
                    scaleLam <- scales.[1]
                    scaleVar <- scales.[2]
                    scaleExp <- scaleLam + scaleVar
                    scaleM <- (int ((float (scaleLam)))) + 2

                    //printf "%A" (initArrayGpu pathToFun)
                    if(((scaleExp* scaleM) / (1024*1024)) < maxMem) then
                        try
                            run (initArrayGpu pathToFun) (initArrayGpu pathToArg1) (initArrayGpu pathToArg2) 2
                        with _ -> printf "error mem\n"
    startAllTests

//let pfunct1 = cuda {
//
//        let! createStartStoreKernel = 
//            <@ fun  (devStore:DevicePtr<int>)
//                    scaleExp
//                    scaleM
//                    scaleVar ->
//                let row = blockDim.y*blockIdx.y + threadIdx.y
//                let column = blockDim.x*blockIdx.x + threadIdx.x
//            
//                if(row < scaleExp && column < scaleM) then 
//                    if(row < scaleVar) then
//                        if(column % scaleM = 0) then
//                            devStore.[row*scaleM + column] <- 1
//                        else
//                            devStore.[row*scaleM + column] <- -1
//                    else
//                        if(column = 0) then
//                            devStore.[row*scaleM + column] <- 2
//                        else 
//                            if(column = 1) then
//                                devStore.[row*scaleM + column] <- row - scaleVar + 1
//                            else 
//                                devStore.[row*scaleM + column] <- -1 @>
//                                |> defineKernelFunc
//
//    //let divUp num den = (num + den - 1) / den
//
//    return PFunc(fun (m:Module) ->
//            let createStartStoreKernel = createStartStoreKernel.Apply m
//            let n = 2*9
//
//            let divUp num den = (num + den - 1) / den
//        
//            use dev_Store = m.Worker.Malloc<int>(n)
//            use dev_rep = m.Worker.Malloc<int>(1)
//            //create startStore
//            let thread : dim3 = new dim3(32,32)
//            let blocks : dim3 = new dim3(divUp 2 thread.x, divUp 9 thread.y)
//            let lpCSSK = LaunchParam(blocks, thread)
//            createStartStoreKernel.Launch lpCSSK dev_Store.Ptr 9 2 6
//
//
//
//            dev_Store.ToHost()
//            //dev_outPut.ToHost()
//
//        ) }

[<EntryPoint>]
let Main(args) =

 //   let worker = Engine.workers.DefaultWorker
  //  use pfuncm = worker.LoadPModule(pfunct)


  //  let dz = pfuncm.Invoke
    
    pathToResFile <- args.[1]
    Start 900 args.[0]//pathTests
    0